#include "geoshaderfactory.h"
#include "global.h"

bool
GeoShaderFactory::loadShader(GLhandleARB &progObj,
			    QString shaderString)
{
  GLhandleARB fragObj = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);  
  glAttachObjectARB(progObj, fragObj);

  // -- vertex shader used only for passing pointpos to fragment shader
  {
    GLhandleARB vertObj = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);  
    glAttachObjectARB(progObj, vertObj);
    QString qstr;
    qstr = "varying vec3 pointpos;\n";
    qstr += "varying vec3 normal;\n";
    qstr += "void main(void)\n";
    qstr += "{\n";
    qstr += "  // Transform vertex position into homogenous clip-space.\n";
    qstr += "  gl_FrontColor = gl_Color;\n";
    qstr += "  gl_BackColor = gl_Color;\n";
    qstr += "  gl_Position = ftransform();\n";
    qstr += "  pointpos = gl_Vertex.xyz;\n";
    qstr += "  normal = gl_NormalMatrix * gl_Normal;\n";
    qstr += "  gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;\n";
    qstr += "  gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n";
    qstr += "}\n";

    int len = qstr.length();
    char *tbuffer = new char[len+1];
    sprintf(tbuffer, qstr.toAscii().data());
    const char *sstr = tbuffer;
    glShaderSourceARB(vertObj, 1, &sstr, NULL);
    delete [] tbuffer;

    GLint compiled;
    glCompileShaderARB(vertObj);
    glGetObjectParameterivARB(vertObj,
			      GL_OBJECT_COMPILE_STATUS_ARB,
			      &compiled);
    if (!compiled)
      {
	GLcharARB str[1000];
	GLsizei len;
	glGetInfoLogARB(vertObj,
			(GLsizei) 1000,
			&len,
			str);

	QMessageBox::information(0,
				 "Error : Vertex Shader",
				 str);
	return false;
    }
  }
    
    
  int len = shaderString.length();
  char *tbuffer = new char[len+1];
  sprintf(tbuffer, shaderString.toAscii().data());
  const char *sstr = tbuffer;
  glShaderSourceARB(fragObj, 1, &sstr, NULL);
  delete [] tbuffer;
  
  GLint compiled;
  glCompileShaderARB(fragObj);
  glGetObjectParameterivARB(fragObj,
			    GL_OBJECT_COMPILE_STATUS_ARB,
			    &compiled);
  if (!compiled)
    {
      GLcharARB str[1000];
      GLsizei len;
      glGetInfoLogARB(fragObj,
		      (GLsizei) 1000,
		      &len,
		      str);

      QMessageBox::information(0,
			       "Error : Fragment Shader",
			       str);
      return false;
    }
  
  
  //----------- link program shader ----------------------
  GLint linked;
  glLinkProgramARB(progObj);
  glGetObjectParameterivARB(progObj, GL_OBJECT_LINK_STATUS_ARB, &linked);
  if (!linked)
    {
      GLcharARB str[1000];
      GLsizei len;
      QMessageBox::information(0,
			       "ProgObj",
			       "error linking texProgObj");
      glGetInfoLogARB(progObj,
		      (GLsizei) 1000,
		      &len,
		      str);
      QMessageBox::information(0,
			       "Error",
			       QString("%1\n%2").arg(len).arg(str));
      return false;
    }
return true;
}


QString
GeoShaderFactory::genDefaultShaderString()
{
  QString shader;

  shader = "varying vec3 pointpos;\n";
  shader += "varying vec3 normal;\n";
  shader += "uniform vec3 eyepos;\n";
  shader += "void main(void)\n";
  shader += "{\n";

  shader += "  vec3 VP = pointpos - gl_LightSource[0].position.xyz;\n";
  shader += "  VP = normalize(VP);\n";
  shader += "  vec3 pnormal = normalize(normal);\n";
  shader += "  float nDotVP = abs(dot(pnormal, VP));\n";
  shader += "  vec3 eye = pointpos - eyepos;\n";
  shader += "  eye = normalize(eye);\n";
  shader += "  vec3 halfVector = normalize(VP + eye);\n";
  shader += "  float nDotHV = abs(dot(pnormal, halfVector));\n";
  shader += "  float pf;\n";
  shader += "  if (nDotVP == 0.0) pf = 0.0;\n";
  shader += "    else pf = pow(nDotHV, gl_FrontMaterial.shininess);\n";
  shader += "  vec4 ambient = gl_LightSource[0].ambient;\n";
  shader += "  vec4 diffuse = gl_LightSource[0].diffuse * nDotVP;\n";
  shader += "  vec4 specular = gl_LightSource[0].specular * pf;\n";
  shader += "  gl_FragColor = gl_FrontLightModelProduct.sceneColor +\n";
  shader += "                 ambient*gl_FrontMaterial.ambient +\n";
  shader += "                 diffuse*gl_FrontMaterial.diffuse +\n";
  shader += "                 specular*gl_FrontMaterial.specular;\n";
  shader += "  gl_FragColor.a = gl_Color.a;\n";

  shader += "}\n";

  return shader;
}

QString
GeoShaderFactory::genHighQualityShaderString(bool shadows,
					     float shadowintensity)
{
  QString shader;

  shader = "varying vec3 pointpos;\n";
  shader += "varying vec3 normal;\n";
  shader += "uniform vec3 eyepos;\n";
  shader += "uniform sampler2DRect shadowTex;\n";
  shader += "void main(void)\n";
  shader += "{\n";

  shader += "  vec3 pnormal = normalize(normal);\n";

  shader += "  vec3 lightvec = pointpos - gl_LightSource[0].position.xyz;\n";
  shader += "  lightvec = normalize(lightvec);\n";
  
  shader += "  float DiffMag = abs(dot(pnormal, lightvec));\n";
  shader += "  vec4 diffuse = gl_LightSource[0].diffuse*DiffMag*gl_FrontMaterial.diffuse;\n";
  
  shader += "  vec3 reflecvec = reflect(lightvec, pnormal);\n";
  shader += "  float Spec = pow(abs(dot(pnormal, reflecvec)), gl_FrontMaterial.shininess);\n";
  shader += "  vec4 specular = gl_LightSource[0].specular*Spec*gl_Color.a;\n";
  
  shader += "  vec4 ambient = gl_LightSource[0].ambient*gl_FrontMaterial.ambient;\n";
  
  shader += "  gl_FragColor = ambient + diffuse + specular;\n";
  shader += "  gl_FragColor.a = gl_Color.a;\n";

  if (shadows)
    {
      //----------------------
      // shadows
      shader += "  vec4 incident_light = texture2DRect(shadowTex, gl_TexCoord[0].xy);\n";
      shader += "  float maxval = 1.0 - incident_light.a;\n";
      if (shadowintensity < 0.95)
	{
	  if (shadowintensity > 0.05)
	    {
	      shader += "  incident_light = maxval*(incident_light + maxval);\n";
	      shader += "  gl_FragColor.rgb *= mix(vec3(1.0,1.0,1.0), ";
	      shader += QString("incident_light.rgb, %1);\n").arg(shadowintensity);
	    }
	}
      else
	shader += "  gl_FragColor.rgb *= maxval*(incident_light.rgb + maxval);\n";
     //----------------------
    }


  shader += "}\n";

  return shader;
}

QString
GeoShaderFactory::genShadowShaderString(float r, float g, float b)
{
  QString shader;
  float maxrgb = qMax(r, qMax(g, b));
  if (maxrgb > 1)
    maxrgb = 1.0/maxrgb;
  else
    maxrgb = 1.0;

  shader += "void main(void)\n";
  shader += "{\n";

  shader += "  gl_FragColor = gl_Color;\n";

  shader += QString("  gl_FragColor.rgba *= vec4(%1, %2, %3, %4);\n").\
                                        arg(r).arg(g).arg(b).arg(maxrgb);
  shader += "}\n";

  return shader;
}

QString
GeoShaderFactory::genSpriteShaderString()
{
  QString shader;

  shader = "varying vec3 pointpos;\n";
  shader += "varying vec3 normal;\n";

  shader += "uniform sampler2D spriteTex;\n";

  shader += "void main(void)\n";
  shader += "{\n";
  shader += "  gl_FragColor = texture2D(spriteTex, gl_TexCoord[0].xy);\n";
  shader += "  gl_FragColor.rgba *= gl_Color.rgba;\n";
  shader += "}\n";
  
  return shader;
}

QString
GeoShaderFactory::genSpriteShadowShaderString(float r, float g, float b)
{
  QString shader;
  float maxrgb = qMax(r, qMax(g, b));
  if (maxrgb > 1)
    maxrgb = 1.0/maxrgb;
  else
    maxrgb = 1.0;

  shader += "uniform sampler2D spriteTex;\n";

  shader += "void main(void)\n";
  shader += "{\n";

  shader += "  gl_FragColor = texture2D(spriteTex, gl_TexCoord[0].xy);\n";
  shader += "  gl_FragColor.rgba *= gl_Color.rgba;\n";

  shader += QString("  gl_FragColor.rgba *= vec4(%1, %2, %3, %4);\n").\
                                        arg(r).arg(g).arg(b).arg(maxrgb);
  shader += "}\n";

  return shader;
}
